package main

/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -ladvapi32
#include <stdlib.h>
#include <string.h>

// C 端枚举定义
typedef enum {
    UNZIP_FORMAT_AUTO = 0,   // 自动识别格式
    UNZIP_FORMAT_ZIP = 1,    // ZIP
    UNZIP_FORMAT_TAR = 2,    // TAR
    UNZIP_FORMAT_TAR_GZ = 3, // TAR.GZ/TGZ
    UNZIP_FORMAT_TAR_BZ2 =4, // TAR.BZ2
    UNZIP_FORMAT_TAR_XZ =5,  // TAR.XZ
    UNZIP_FORMAT_TAR_ZST =6, // TAR.ZST
    UNZIP_FORMAT_7Z =7,      // 7Z
    UNZIP_FORMAT_RAR =8,     // RAR
    UNZIP_FORMAT_GZ =9,      // GZ（单文件）
    UNZIP_FORMAT_BZ2 =10,    // BZ2（单文件）
    UNZIP_FORMAT_XZ =11,     // XZ（单文件）
    UNZIP_FORMAT_ZST =12     // ZST（单文件）
} UnzipFormat;

typedef enum {
    UNZIP_OK = 0,            // 成功
    UNZIP_ERR_OPEN_FILE = -1,// 打开文件失败
    UNZIP_ERR_FORMAT = -2,    // 格式不支持/识别失败
    UNZIP_ERR_EXTRACT = -3,  // 解压过程错误
    UNZIP_ERR_MEM = -4,      // 内存分配失败
    UNZIP_ERR_PERM = -5      // 权限不足
} UnzipErrCode;

// C 侧函数声明（无 const，与 cgo 导出匹配）
UnzipErrCode UnzipFile(char* inputPath, char* outputDir);
UnzipErrCode UnzipFileWithFormat(char* inputPath, char* outputDir, UnzipFormat format);
char* UnzipGetLastError();
*/
import "C"
import (
	"archive/tar"
	"archive/zip"
	"compress/gzip"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/bodgit/sevenzip"         // 7z解压
	"github.com/dsnet/compress/bzip2"    // tar.bz2解压（第三方库）
	"github.com/klauspost/compress/zstd" // zstd解压
	"github.com/nwaples/rardecode"       // RAR解压
	"github.com/xi2/xz"                  // tar.xz解压
)

// --------------------------
// 1. 类型映射与常量定义（无修改）
// --------------------------
type UnzipFormat C.UnzipFormat
type UnzipErrCode C.UnzipErrCode

const (
	UNZIP_FORMAT_AUTO     UnzipFormat = C.UNZIP_FORMAT_AUTO
	UNZIP_FORMAT_ZIP      UnzipFormat = C.UNZIP_FORMAT_ZIP
	UNZIP_FORMAT_TAR      UnzipFormat = C.UNZIP_FORMAT_TAR
	UNZIP_FORMAT_TAR_GZ   UnzipFormat = C.UNZIP_FORMAT_TAR_GZ
	UNZIP_FORMAT_TAR_BZ2  UnzipFormat = C.UNZIP_FORMAT_TAR_BZ2
	UNZIP_FORMAT_TAR_XZ   UnzipFormat = C.UNZIP_FORMAT_TAR_XZ
	UNZIP_FORMAT_TAR_ZST  UnzipFormat = C.UNZIP_FORMAT_TAR_ZST
	UNZIP_FORMAT_7Z       UnzipFormat = C.UNZIP_FORMAT_7Z
	UNZIP_FORMAT_RAR      UnzipFormat = C.UNZIP_FORMAT_RAR
	UNZIP_FORMAT_GZ       UnzipFormat = C.UNZIP_FORMAT_GZ
	UNZIP_FORMAT_BZ2      UnzipFormat = C.UNZIP_FORMAT_BZ2
	UNZIP_FORMAT_XZ       UnzipFormat = C.UNZIP_FORMAT_XZ
	UNZIP_FORMAT_ZST      UnzipFormat = C.UNZIP_FORMAT_ZST

	UNZIP_OK              UnzipErrCode = C.UNZIP_OK
	UNZIP_ERR_OPEN_FILE   UnzipErrCode = C.UNZIP_ERR_OPEN_FILE
	UNZIP_ERR_FORMAT      UnzipErrCode = C.UNZIP_ERR_FORMAT
	UNZIP_ERR_EXTRACT     UnzipErrCode = C.UNZIP_ERR_EXTRACT
	UNZIP_ERR_MEM         UnzipErrCode = C.UNZIP_ERR_MEM
	UNZIP_ERR_PERM        UnzipErrCode = C.UNZIP_ERR_PERM
)

// --------------------------
// 2. 全局错误管理（无修改）
// --------------------------
var (
	lastErr  string
	errMutex sync.Mutex
)

func setLastErr(err string) {
	errMutex.Lock()
	defer errMutex.Unlock()
	lastErr = err
}

func resetLastErr() {
	setLastErr("")
}

// --------------------------
// 3. 导出函数（无修改）
// --------------------------
//export UnzipGetLastError
func UnzipGetLastError() *C.char {
	errMutex.Lock()
	defer errMutex.Unlock()
	return C.CString(lastErr)
}

//export UnzipFile
func UnzipFile(inputPath *C.char, outputDir *C.char) C.UnzipErrCode {
	goInputPath := C.GoString(inputPath)
	goOutputDir := C.GoString(outputDir)

	format := detectFormat(goInputPath)
	if format == UNZIP_FORMAT_AUTO {
		commonFormats := []UnzipFormat{
			UNZIP_FORMAT_ZIP, UNZIP_FORMAT_7Z, UNZIP_FORMAT_RAR,
			UNZIP_FORMAT_TAR_GZ, UNZIP_FORMAT_TAR_BZ2, UNZIP_FORMAT_TAR_XZ,
		}
		for _, f := range commonFormats {
			if code := unzipWithFormat(goInputPath, goOutputDir, f); code == UNZIP_OK {
				return C.UnzipErrCode(code)
			}
		}
		setLastErr("自动识别格式失败，不支持该压缩类型")
		return C.UNZIP_ERR_FORMAT
	}

	return C.UnzipErrCode(unzipWithFormat(goInputPath, goOutputDir, format))
}

//export UnzipFileWithFormat
func UnzipFileWithFormat(inputPath *C.char, outputDir *C.char, format C.UnzipFormat) C.UnzipErrCode {
	goInputPath := C.GoString(inputPath)
	goOutputDir := C.GoString(outputDir)
	goFormat := UnzipFormat(format)

	return C.UnzipErrCode(unzipWithFormat(goInputPath, goOutputDir, goFormat))
}

// --------------------------
// 4. 核心工具函数（无修改）
// --------------------------
func detectFormat(inputPath string) UnzipFormat {
	ext := strings.ToLower(filepath.Ext(inputPath))
	switch ext {
	case ".zip":
		return UNZIP_FORMAT_ZIP
	case ".7z":
		return UNZIP_FORMAT_7Z
	case ".rar":
		return UNZIP_FORMAT_RAR
	case ".gz":
		if strings.Contains(strings.ToLower(inputPath), ".tar.") || strings.HasSuffix(strings.ToLower(inputPath), ".tgz") {
			return UNZIP_FORMAT_TAR_GZ
		}
		return UNZIP_FORMAT_GZ
	case ".bz2":
		if strings.Contains(strings.ToLower(inputPath), ".tar.") {
			return UNZIP_FORMAT_TAR_BZ2
		}
		return UNZIP_FORMAT_BZ2
	case ".xz":
		if strings.Contains(strings.ToLower(inputPath), ".tar.") {
			return UNZIP_FORMAT_TAR_XZ
		}
		return UNZIP_FORMAT_XZ
	case ".zst":
		if strings.Contains(strings.ToLower(inputPath), ".tar.") {
			return UNZIP_FORMAT_TAR_ZST
		}
		return UNZIP_FORMAT_ZST
	case ".tar":
		return UNZIP_FORMAT_TAR
	case ".tgz":
		return UNZIP_FORMAT_TAR_GZ
	default:
		return UNZIP_FORMAT_AUTO
	}
}

func createOutputDir(outputDir string) error {
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		return fmt.Errorf("创建目录失败: %w", err)
	}
	return nil
}

func extractSingleFile(r io.Reader, outputPath string) error {
	outFile, err := os.OpenFile(outputPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		return fmt.Errorf("创建输出文件失败: %w", err)
	}
	defer outFile.Close()

	_, err = io.Copy(outFile, r)
	if err != nil {
		return fmt.Errorf("写入文件失败: %w", err)
	}
	return nil
}

func extractTar(r io.Reader, outputDir string) error {
	tarReader := tar.NewReader(r)
	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return fmt.Errorf("读取tar头失败: %w", err)
		}

		targetPath := filepath.Join(outputDir, header.Name)
		switch header.Typeflag {
		case tar.TypeDir:
			if err := os.MkdirAll(targetPath, 0755); err != nil {
				return fmt.Errorf("创建tar目录失败: %w", err)
			}
		case tar.TypeReg:
			if err := extractSingleFile(tarReader, targetPath); err != nil {
				return fmt.Errorf("解压tar文件失败: %w", err)
			}
		}
	}
	return nil
}

// --------------------------
// 5. 核心解压逻辑（关键修复：第三方库调用格式）
// --------------------------
func unzipWithFormat(inputPath, outputDir string, format UnzipFormat) UnzipErrCode {
	resetLastErr()

	// 打开输入文件
	inFile, err := os.Open(inputPath)
	if err != nil {
		setLastErr(fmt.Sprintf("打开文件失败: %v", err))
		return UNZIP_ERR_OPEN_FILE
	}
	defer inFile.Close()

	// 创建输出目录
	if err := createOutputDir(outputDir); err != nil {
		setLastErr(fmt.Sprintf("创建输出目录失败: %v", err))
		return UNZIP_ERR_PERM
	}

	// 按格式解压（修复所有第三方库调用错误）
	switch format {
	case UNZIP_FORMAT_ZIP:
		zipReader, err := zip.OpenReader(inputPath)
		if err != nil {
			setLastErr(fmt.Sprintf("读取ZIP失败: %v", err))
			return UNZIP_ERR_FORMAT
		}
		defer zipReader.Close()
		for _, file := range zipReader.File {
			filePath := filepath.Join(outputDir, file.Name)
			if file.FileInfo().IsDir() {
				os.MkdirAll(filePath, 0755)
				continue
			}
			fileReader, err := file.Open()
			if err != nil {
				setLastErr(fmt.Sprintf("打开ZIP内文件失败: %v", err))
				return UNZIP_ERR_EXTRACT
			}
			defer fileReader.Close()
			if err := extractSingleFile(fileReader, filePath); err != nil {
				setLastErr(fmt.Sprintf("解压ZIP文件失败: %v", err))
				return UNZIP_ERR_EXTRACT
			}
		}

	case UNZIP_FORMAT_TAR:
		if err := extractTar(inFile, outputDir); err != nil {
			setLastErr(fmt.Sprintf("解压TAR失败: %v", err))
			return UNZIP_ERR_EXTRACT
		}

	case UNZIP_FORMAT_TAR_GZ:
		gzReader, err := gzip.NewReader(inFile)
		if err != nil {
			setLastErr(fmt.Sprintf("读取GZ失败: %v", err))
			return UNZIP_ERR_FORMAT
		}
		defer gzReader.Close()
		if err := extractTar(gzReader, outputDir); err != nil {
			setLastErr(fmt.Sprintf("解压TAR.GZ失败: %v", err))
			return UNZIP_ERR_EXTRACT
		}

	case UNZIP_FORMAT_TAR_BZ2:
		// 【修复1】dsnet/bzip2.NewReader 需要 2 个参数，返回 2 个值
		bz2Reader, err := bzip2.NewReader(inFile, nil) // 第二个参数传 nil（默认配置）
		if err != nil {
			setLastErr(fmt.Sprintf("创建BZ2读取器失败: %v", err))
			return UNZIP_ERR_FORMAT
		}
		if err := extractTar(bz2Reader, outputDir); err != nil {
			setLastErr(fmt.Sprintf("解压TAR.BZ2失败: %v", err))
			return UNZIP_ERR_EXTRACT
		}

	case UNZIP_FORMAT_TAR_XZ:
		// 【修复2】xi2/xz.NewReader 第二个参数是 uint32（字典大小），不能传 nil
		xzReader, err := xz.NewReader(inFile, 0) // 0 表示使用默认字典大小
		if err != nil {
			setLastErr(fmt.Sprintf("创建XZ读取器失败: %v", err))
			return UNZIP_ERR_FORMAT
		}
		if err := extractTar(xzReader, outputDir); err != nil {
			setLastErr(fmt.Sprintf("解压TAR.XZ失败: %v", err))
			return UNZIP_ERR_EXTRACT
		}

	case UNZIP_FORMAT_TAR_ZST:
		zstReader, err := zstd.NewReader(inFile)
		if err != nil {
			setLastErr(fmt.Sprintf("创建ZST读取器失败: %v", err))
			return UNZIP_ERR_FORMAT
		}
		defer zstReader.Close()
		if err := extractTar(zstReader, outputDir); err != nil {
			setLastErr(fmt.Sprintf("解压TAR.ZST失败: %v", err))
			return UNZIP_ERR_EXTRACT
		}

	case UNZIP_FORMAT_7Z:
		// 【修复3】bodgit/sevenzip.OpenReader 接收 string（文件路径），不是 *os.File
		szReader, err := sevenzip.OpenReader(inputPath)
		if err != nil {
			setLastErr(fmt.Sprintf("读取7Z失败: %v", err))
			return UNZIP_ERR_FORMAT
		}
		defer szReader.Close()
		for _, file := range szReader.File {
			filePath := filepath.Join(outputDir, file.Name)
			// 【修复4】sevenzip.File 无 IsDir 字段，用 FileInfo().IsDir() 判断
			if file.FileInfo().IsDir() {
				os.MkdirAll(filePath, 0755)
				continue
			}
			fileReader, err := file.Open()
			if err != nil {
				setLastErr(fmt.Sprintf("打开7Z内文件失败: %v", err))
				return UNZIP_ERR_EXTRACT
			}
			defer fileReader.Close()
			if err := extractSingleFile(fileReader, filePath); err != nil {
				setLastErr(fmt.Sprintf("解压7Z文件失败: %v", err))
				return UNZIP_ERR_EXTRACT
			}
		}

	case UNZIP_FORMAT_RAR:
		rarReader, err := rardecode.NewReader(inFile, "")
		if err != nil {
			setLastErr(fmt.Sprintf("创建RAR读取器失败: %v", err))
			return UNZIP_ERR_FORMAT
		}
		for {
			header, err := rarReader.Next()
			if err == io.EOF {
				break
			}
			if err != nil {
				setLastErr(fmt.Sprintf("读取RAR头失败: %v", err))
				return UNZIP_ERR_EXTRACT
			}
			targetPath := filepath.Join(outputDir, header.Name)
			if header.IsDir {
				os.MkdirAll(targetPath, 0755)
				continue
			}
			if err := extractSingleFile(rarReader, targetPath); err != nil {
				setLastErr(fmt.Sprintf("解压RAR文件失败: %v", err))
				return UNZIP_ERR_EXTRACT
			}
		}

	case UNZIP_FORMAT_GZ:
		outputPath := strings.TrimSuffix(inputPath, ".gz")
		outputPath = filepath.Join(outputDir, filepath.Base(outputPath))
		gzReader, err := gzip.NewReader(inFile)
		if err != nil {
			setLastErr(fmt.Sprintf("创建GZ读取器失败: %v", err))
			return UNZIP_ERR_FORMAT
		}
		defer gzReader.Close()
		if err := extractSingleFile(gzReader, outputPath); err != nil {
			setLastErr(fmt.Sprintf("解压GZ单文件失败: %v", err))
			return UNZIP_ERR_EXTRACT
		}

	case UNZIP_FORMAT_BZ2:
		// 【修复5】同 TAR.BZ2，补充参数和返回值处理
		outputPath := strings.TrimSuffix(inputPath, ".bz2")
		outputPath = filepath.Join(outputDir, filepath.Base(outputPath))
		bz2Reader, err := bzip2.NewReader(inFile, nil)
		if err != nil {
			setLastErr(fmt.Sprintf("创建BZ2读取器失败: %v", err))
			return UNZIP_ERR_FORMAT
		}
		if err := extractSingleFile(bz2Reader, outputPath); err != nil {
			setLastErr(fmt.Sprintf("解压BZ2单文件失败: %v", err))
			return UNZIP_ERR_EXTRACT
		}

	case UNZIP_FORMAT_XZ:
		// 【修复6】同 TAR.XZ，第二个参数传 0（默认）
		outputPath := strings.TrimSuffix(inputPath, ".xz")
		outputPath = filepath.Join(outputDir, filepath.Base(outputPath))
		xzReader, err := xz.NewReader(inFile, 0)
		if err != nil {
			setLastErr(fmt.Sprintf("创建XZ读取器失败: %v", err))
			return UNZIP_ERR_FORMAT
		}
		if err := extractSingleFile(xzReader, outputPath); err != nil {
			setLastErr(fmt.Sprintf("解压XZ单文件失败: %v", err))
			return UNZIP_ERR_EXTRACT
		}

	case UNZIP_FORMAT_ZST:
		outputPath := strings.TrimSuffix(inputPath, ".zst")
		outputPath = filepath.Join(outputDir, filepath.Base(outputPath))
		zstReader, err := zstd.NewReader(inFile)
		if err != nil {
			setLastErr(fmt.Sprintf("创建ZST读取器失败: %v", err))
			return UNZIP_ERR_FORMAT
		}
		defer zstReader.Close()
		if err := extractSingleFile(zstReader, outputPath); err != nil {
			setLastErr(fmt.Sprintf("解压ZST单文件失败: %v", err))
			return UNZIP_ERR_EXTRACT
		}

	default:
		setLastErr(fmt.Sprintf("不支持的格式: %d", format))
		return UNZIP_ERR_FORMAT
	}

	return UNZIP_OK
}

func main() {} // 空main，仅用于编译DLL